import 'package:flutter/material.dart';
import 'package:flutter_canvas/widget/comm.dart';
import 'package:flutter_canvas/widget/unity.dart';
import 'package:vector_math/vector_math_64.dart' hide Colors;

class Anim57Page extends StatefulWidget {
  final String title;

  Anim57Page({Key? key, required this.title}) : super(key: key);

  @override
  _Anim57PageState createState() => _Anim57PageState();
}

class _Anim57PageState extends State<Anim57Page> {
  Scene? scene;

  void _onSceneCreated(Scene scene) {
    this.scene = scene;
    scene.camera.position.setFrom(Vector3(0, 0, 1000));
    scene.camera.updateTransform();
  }

  Actor makeBlock(
      {required String name,
      required Vector3 position,
      required double size,
      required List<Actor> faces}) {
    final double radius = size / 2 - size * 0.0015;
    return Actor(name: name, position: position, children: [
      Actor(
          name: faces[0].name,
          position: Vector3(0, 0, radius),
          rotation: Vector3(0, 0, 0),
          width: size,
          height: size,
          widget: faces[0].widget,
          children: faces[0].children),
      Actor(
          name: faces[1].name,
          position: Vector3(radius, 0, 0),
          rotation: Vector3(0, 90, 0),
          width: size,
          height: size,
          widget: faces[1].widget,
          children: faces[1].children),
      Actor(
          name: faces[2].name,
          position: Vector3(0, 0, -radius),
          rotation: Vector3(0, 180, 0),
          width: size,
          height: size,
          widget: faces[2].widget,
          children: faces[2].children),
      Actor(
          name: faces[3].name,
          position: Vector3(-radius, 0, 0),
          rotation: Vector3(0, 270, 0),
          width: size,
          height: size,
          widget: faces[3].widget,
          children: faces[3].children),
      Actor(
          name: faces[4].name,
          position: Vector3(0, -radius, 0),
          rotation: Vector3(90, 0, 0),
          width: size,
          height: size,
          widget: faces[4].widget,
          children: faces[4].children),
      Actor(
          name: faces[5].name,
          position: Vector3(0, radius, 0),
          rotation: Vector3(270, 0, 0),
          width: size,
          height: size,
          widget: faces[5].widget,
          children: faces[5].children),
    ]);
  }

  Widget makeBlockFace(double size, String name, int layer) {
    return Container(
      width: size,
      height: size,
      decoration: BoxDecoration(
        border: Border.all(color: Colors.grey, width: size * 0.05),
        image: DecorationImage(
          image: AssetImage("assets/$name"),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    double blockSize = MediaQuery.of(context).size.width / 0.7;
    return Scaffold(
      appBar: appBar(widget.title),
      body: Stage(
        onSceneCreated: _onSceneCreated,
        children: List.generate(3, (layer) {
          return makeBlock(
            name: layer.toString(),
            position: Vector3(0, 0, 0),
            size: blockSize * (layer * 0.5),
            faces: List.generate(
              6,
              (index) => Actor(
                widget:
                    makeBlockFace(blockSize, "logo${layer % 2 + 1}.png", layer),
                children: [
                  Actor(
                    position: Vector3(0, 0, 0),
                    width: blockSize,
                    height: blockSize,
                  ),
                ],
              ),
            ).toList(),
          );
        }).toList(),
      ),
    );
  }
}

typedef void SceneCreatedCallback(Scene scene);

class Stage extends StatefulWidget {
  Stage({
    Key? key,
    this.interactive = true,
    this.onSceneCreated,
    this.children = const <Actor>[],
  }) : super(key: key);

  final bool interactive;
  final SceneCreatedCallback? onSceneCreated;
  final List<Actor> children;

  @override
  _StageState createState() => _StageState();
}

class _StageState extends State<Stage> {
  late Scene scene;
  Offset _lastFocalPoint = Offset.zero;

  void _handleScaleStart(DragStartDetails details) {
    _lastFocalPoint = details.localPosition;
  }

  void _handleScaleUpdate(DragUpdateDetails details) {
//    if (details.scale != 1.0) {
//      scene.camera.position.scale(1.0 + (1.0 - details.scale) * 0.01);
//    }
    scene.camera.trackBall(_lastFocalPoint, details.localPosition, 1.5);
    _lastFocalPoint = details.localPosition;
    setState(() {});
  }

  /// Transform from homonegenous coordinates to the normalized device coordinates，and then transform to viewport.
  void applyViewportTransform(
      Vector4 v, double viewportWidth, double viewportHeight) {
    final storage = v.storage;
    //perspective division,
    final double w = storage[3];
    final double x = storage[0] / w;
    final double y = storage[1] / w;
    final double z = storage[2] / w;
    storage[0] = x;
    storage[1] = y;
    storage[2] = z;
  }

  void transformActor(List<Actor> list, Actor actor, Matrix4 transform) {
    final Matrix4 _transform = transform * actor.transform;
    final Vector4 v = Vector4.identity();
    v.applyMatrix4(_transform);
    applyViewportTransform(
        v, scene.camera.viewportWidth, scene.camera.viewportHeight);
    v.xyz.copyInto(actor.transformedPosition);
    _transform.copyInto(actor.transformedMatrix);
    list.add(actor);

    for (int i = 0; i < actor.children.length; i++) {
      transformActor(list, actor.children[i], _transform);
    }
  }

  @override
  void didUpdateWidget(Stage oldWidget) {
    super.didUpdateWidget(oldWidget);
    scene.updateChildren(widget.children);
  }

  @override
  void initState() {
    super.initState();
    scene = Scene(onUpdate: () => setState(() {}), children: widget.children);
    if (widget.onSceneCreated != null) widget.onSceneCreated!(scene);
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        scene.camera.viewportWidth = constraints.maxWidth;
        scene.camera.viewportHeight = constraints.maxHeight;
        final List<Actor> children = [];
        final Matrix4 transform =
            scene.camera.projectionMatrix * scene.camera.transform;
        transformActor(children, scene.world, transform);

        children.sort((Actor a, Actor b) {
          final double az = a.transformedPosition.z;
          final double bz = b.transformedPosition.z;
          if (bz > az) return 1;
          if (bz < az) return -1;
          return 0;
        });

        List<Widget> widgets = [];
        for (int i = 0; i < children.length; i++) {
          final Actor child = children[i];
          final newChild = Positioned(
            left: constraints.maxWidth / 2 - child.width * child.orgin.dx,
            top: constraints.maxHeight / 2 - child.height * child.orgin.dy,
            width: child.width,
            height: child.height,
            child: Transform(
              origin: Offset(
                  child.width * child.orgin.dx, child.height * child.orgin.dy),
              transform: child.transformedMatrix,
              child: child.widget,
            ),
          );
          widgets.add(newChild);
        }

        if (widget.interactive) {
          return GestureDetector(
//            onScaleStart: _handleScaleStart,
//            onScaleUpdate: _handleScaleUpdate,
            onPanStart: _handleScaleStart,
            onPanUpdate: _handleScaleUpdate,
            child: Stack(children: widgets),
          );
        }
        return Stack(children: widgets);
      },
    );
  }
}
